#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include "x264.h"

int compress_frame(x264_t *encoder, x264_picture_t *pic_in, uint8_t *yuv_frame, uint8_t *h264_frame, size_t yuv_size)
{
	size_t i;
	int is_u = 1, y_index = 0, u_index = 0, v_index = 0;
	x264_nal_t *nal;
	x264_picture_t pic_out;
	int nnal = -1;
	int len = 0;

	for(i = 0; i < yuv_size; ++i)
	{
		if(i % 2 == 0)
		{
			*(pic_in->img.plane[0] + y_index++) = *yuv_frame;
		}else{
			if(is_u){
			*(pic_in->img.plane[1] + u_index++) = *yuv_frame;
			is_u = 0;
			}else{
				*(pic_in->img.plane[2] + v_index++) = *yuv_frame;
				is_u = 1;
			}
		}
		yuv_frame++;
	}

	if(x264_encoder_encode(encoder, &nal, &nnal, pic_in, &pic_out) < 0)		/* nnal: the number of segment in pic_in */
		return -1;
	
	for(i = 0; i < nnal; i++)
	{
		memcpy(h264_frame, nal[i].p_payload, nal[i].i_payload);		/* copy one segment per time */
		h264_frame += nal[i].i_payload;
		len += nal[i].i_payload;
	}
	
	return len;
}

int main(int argc, char *argv[])
{
	if(argc < 3 || strcmp(argv[1],"--help") == 0)
	{
		fprintf(stderr,"%s in.yuv out.h264\n",argv[0]);
		exit(1);
	}
	
	int width = 640;
	int height = 480;
	int len;									/* h264 picture length */
	int j = 1;									/* count from 1 */
	int inf, outf;
	x264_t *encoder;
	x264_picture_t *pic_in = NULL;
	x264_param_t *param;
	
	size_t yuv_size = width * height * 2;			/* yuv422 length */
    uint8_t *yuv_frame = malloc(yuv_size);
	uint8_t *h264_frame = malloc(yuv_size);	
	
/* initailize fd */
	inf = open(argv[1], O_RDONLY);
	if (inf < 0) {
		return -1;
	}
	outf = open(argv[2], O_CREAT | O_WRONLY | O_APPEND, 00644);
	if (outf < 0) {
		return -1;
	}
	
/* initailize encoder */
	param = (x264_param_t *) malloc(sizeof(x264_param_t));
	x264_param_default(param); 		// fill up by default parameters
	x264_param_default_preset(param, "veryfast", "zerolatency");
	param->i_threads = 4;
	param->i_width = width;
	param->i_height = height;
	param->i_fps_num = 5;
	param->i_fps_den = 1;
	param->rc.i_lookahead = 0; 	/* buffering ahead */

	param->i_keyint_max = 300;
	param->i_keyint_min = X264_KEYINT_MIN_AUTO;


	x264_param_apply_profile(param, "baseline");
	encoder = x264_encoder_open(param);
	
/* initailize pic_in */
	pic_in = (x264_picture_t *) malloc(sizeof(x264_picture_t));
	x264_picture_alloc(pic_in, X264_CSP_I422, width, height);
	pic_in->img.i_csp = X264_CSP_I422;
	pic_in->img.i_plane = 3;

/* encoding */
	while (read(inf, yuv_frame, yuv_size) > 0) 						/* 一次读 1 帧 yuv frame大小 */
	{
		len = compress_frame(encoder, pic_in, yuv_frame, h264_frame, yuv_size);		/* 把这一帧 yuv 以 YUYV序列 填充到 pic_in plane中 */
		pic_in->i_pts++;
		if(len > 0)
		{
			write(outf, h264_frame, len);
			printf("\e[32m --------- %dth frame --------\e[0m\n",j++);
		}
		else
		{
			perror("encoding failed");
			exit(errno);
		}
	}
	
/* clean */
	x264_encoder_close(encoder);
	close(inf);
	close(outf);
	free(yuv_frame);
	return 0;
}